"""
This module provides tools for interacting with the GitHub API.
It includes functions for authentication, searching repositories,
listing branches, and downloading repository code.
"""

import logging
import os
import shutil
import zipfile
from typing import Any, Optional

import requests

from ..utils import _create_github_headers, _get_auth_token, _parse_repo_path
from .git_ops import initialize_git_repo

logger = logging.getLogger("plumber-agent")

# GitHub API base URL
API_BASE_URL = "https://api.github.com"

# Define the default download directory relative to this file's location
DEFAULT_DOWNLOAD_PATH = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", "github_downloads")
)


def authenticate_github(token: str = "") -> dict[str, Any]:
    """Authenticate with GitHub using a Personal Access Token."""
    token = _get_auth_token(token)
    if not token:
        return {
            "status": "error",
            "message": "Authentication failed: No token provided.",
        }
    headers = _create_github_headers(token)
    try:
        response = requests.get(f"{API_BASE_URL}/user", headers=headers, timeout=60)
        response.raise_for_status()
        user_data = response.json()
        return {
            "status": "success",
            "message": f"Authenticated as {user_data.get('login')}",
            "user": user_data.get("login"),
        }
    except requests.exceptions.Timeout as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        msg = "Request timed out. GitHub did not respond within the expected time."
        return {"status": "error", "message": msg}
    except requests.exceptions.HTTPError as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        if e.response.status_code == 401:
            msg = "Authentication failed (401): Bad credentials. Please check your token."
            return {"status": "error", "message": msg}
        msg = f"Authentication failed ({e.response.status_code}): {e.response.text}"
        return {"status": "error", "message": msg}
    except Exception as e:  # pylint: disable=broad-exception-caught
        logger.error("An error occurred: %s", e, exc_info=True)
        msg = f"An unexpected authentication error occurred: {str(e)}"
        return {"status": "error", "message": msg}


def search_repositories(repo_name: str, token: str = "") -> dict[str, Any]:
    """Search for repositories on GitHub using a query string."""
    token = _get_auth_token(token)
    headers = _create_github_headers(token)
    try:
        response = requests.get(
            f"{API_BASE_URL}/search/repositories",
            headers=headers,
            params={"q": repo_name},
            timeout=10,
        )
        response.raise_for_status()
        items = [item["full_name"] for item in response.json().get("items", [])]
        return {"status": "success", "count": len(items), "results": items}
    except requests.exceptions.Timeout as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        msg = "Request timed out. GitHub did not respond within the expected time."
        return {"status": "error", "message": msg}
    except requests.exceptions.HTTPError as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        msg = f"Search failed ({e.response.status_code}): {e.response.text}"
        return {"status": "error", "message": msg}
    except Exception as e:  # pylint: disable=broad-exception-caught
        logger.error("An error occurred: %s", e, exc_info=True)
        return {
            "status": "error",
            "message": f"An error occurred during search: {str(e)}",
        }


def list_branches(repository: str, token: str = "") -> dict[str, Any]:
    """List all branches for a given repository."""
    owner, repo = _parse_repo_path(repository)
    if not owner:
        return {
            "status": "error",
            "message": "Invalid repository format. Use 'owner/repo'.",
        }
    token = _get_auth_token(token)
    headers = _create_github_headers(token)
    try:
        response = requests.get(
            f"{API_BASE_URL}/repos/{owner}/{repo}/branches", headers=headers, timeout=10
        )
        response.raise_for_status()
        branches = [branch["name"] for branch in response.json()]
        return {
            "status": "success",
            "repository": f"{owner}/{repo}",
            "branches": branches,
        }
    except requests.exceptions.Timeout as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        return {
            "status": "error",
            "message": "Request timed out. GitHub did not respond in time.",
        }
    except requests.exceptions.HTTPError as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        if e.response.status_code == 404:
            msg = "Could not list branches (404): Repository not found."
            return {"status": "error", "message": msg}
        msg = f"Failed to list branches ({e.response.status_code}): {e.response.text}"
        return {"status": "error", "message": msg}
    except Exception as e:  # pylint: disable=broad-exception-caught
        logger.error("An error occurred: %s", e, exc_info=True)
        msg = f"An error occurred while listing branches: {str(e)}"
        return {"status": "error", "message": msg}


def download_repository(
    repository: str,
    branch: str = "main",
    download_path: Optional[str] = None,
    token: str = "",
    init_git: bool = True,
) -> dict[str, Any]:
    """
    Download a GitHub repository.

    Args:
        repository: Repository URL or 'owner/repo' format
        branch: Branch to download (default: main)
        download_path: Local path to download to.
                       Defaults to 'agent/agents/github_agent/github_downloads/'.
        token: GitHub Personal Access Token
        init_git: Whether to initialize Git repository (default: True)

    Returns:
        Dict with operation result
    """
    owner, repo = _parse_repo_path(repository)
    if not owner:
        return {
            "status": "error",
            "message": "Invalid repository format. Use 'owner/repo'.",
        }

    # Set the default download path if not provided
    if download_path is None:
        download_path = DEFAULT_DOWNLOAD_PATH

    token = _get_auth_token(token)
    headers = _create_github_headers(token)
    zip_url = f"{API_BASE_URL}/repos/{owner}/{repo}/zipball/{branch}"

    try:
        response = requests.get(zip_url, headers=headers, stream=True, timeout=10)
        response.raise_for_status()

        # Ensure the base download directory exists
        os.makedirs(download_path, exist_ok=True)
        zip_path = os.path.join(download_path, f"{repo}-{branch}.zip")

        with open(zip_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)

        temp_extract_path = os.path.join(download_path, f"_temp_{repo}")
        with zipfile.ZipFile(zip_path, "r") as zip_ref:
            root_folder_in_zip = zip_ref.namelist()[0]
            zip_ref.extractall(temp_extract_path)

        extracted_root = os.path.join(temp_extract_path, root_folder_in_zip)
        final_path = os.path.join(download_path, f"{repo}-{branch}")

        if os.path.exists(final_path):
            shutil.rmtree(final_path)
        shutil.move(extracted_root, final_path)
        shutil.rmtree(temp_extract_path)
        os.remove(zip_path)

        result = {
            "status": "success",
            "message": "Repository downloaded and extracted successfully.",
            "repository": f"{owner}/{repo}",
            "branch": branch,
            "path": final_path,
        }

        # Initialize Git repository if requested
        if init_git:
            git_result = initialize_git_repo(final_path)
            result["git_initialized"] = git_result["status"] == "success"
            result["git_message"] = git_result["message"]

        return result
    except requests.exceptions.Timeout as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        msg = "Download request timed out. GitHub did not respond within the expected time."
        return {"status": "error", "message": msg}
    except requests.exceptions.HTTPError as e:
        logger.error("An error occurred: %s", e, exc_info=True)
        if e.response.status_code == 404:
            msg = "Download failed (404): Repository or branch not found."
            return {"status": "error", "message": msg}
        msg = f"Download failed ({e.response.status_code}): {e.response.text}"
        return {"status": "error", "message": msg}
    except Exception as e:  # pylint: disable=broad-exception-caught
        logger.error("An error occurred: %s", e, exc_info=True)
        msg = f"An error occurred during download: {str(e)}"
        return {"status": "error", "message": msg}
